package com.esiag.loadbalancer.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.esiag.pdsteam.loadbalancer.ILoadBalancer;

public class LoadBalancer<TEntity, TInfo> implements ILoadBalancer<TEntity, TInfo>{

	private Notifier<TInfo> mNotifier;
	private Subscriber<TEntity> mSubscriber;
	
	public LoadBalancer() {
		mNotifier = new Notifier<TInfo>();
		mSubscriber = new Subscriber<TEntity>();
		mNotifier.setLoadBalancer(this);
		//mNotifier.setLoadBalander(this);
	}
	
	public TEntity loadBalance(TInfo pInfo) {
		TEntity bestEntity = null;
		for(TEntity entity : mSubscriber.getEntities()){
			if(bestEntity == null || whosBest(bestEntity, entity)){
				bestEntity = entity;
			}
		}
		sendProcess(bestEntity, pInfo);
		return bestEntity;
	}
	
	public void sendProcess(final TEntity pEntity, final TInfo pInfo){
		new Thread(new Runnable() {
			
			public void run() {
				try {
					Method method = pEntity.getClass().getDeclaredMethod("sendInfo", new Class[]{ pInfo.getClass() });
					method.invoke(pEntity, pInfo);
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		
		}).start();
	
	}
	
	public boolean whosBest(TEntity pNewEntity, TEntity pOldEntity){
		int i = 0;
		Method[] mtds = pNewEntity.getClass().getDeclaredMethods();
		while(i < mtds.length){
			try {
				if(mtds[i].getName().equals("isBest")){
					return (Boolean)mtds[i].invoke(pNewEntity, pOldEntity);
				}
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}	
		}
		return false;
	}
	
	public Notifier<TInfo> getNotifier() {
		return mNotifier;
	}
	
	public Subscriber<TEntity> getSubscriber() {
		return mSubscriber;
	}
}
